/// extracting each file in turn to the location specified by the entry's
/// path name.
///
- /// This operation is relatively sensitive in that it will not write files
- /// outside of the path specified by `dst`. Files in the archive which have
- /// a '..' in their path are skipped during the unpacking process.
+ /// # Security
+ ///
+ /// A best-effort is made to prevent writing files outside `dst` (paths
+ /// containing `..` are skipped, symlinks are validated). However, there
+ /// have been historical bugs in this area, and more may exist. For this
+ /// reason, when processing untrusted archives, stronger sandboxing is
+ /// encouraged: e.g. the [`cap-std`] crate and/or OS-level
+ /// containerization/virtualization.
+ ///
+ /// If `dst` does not exist, it is created. Unpacking into an existing
+ /// directory merges content. This function assumes `dst` is not
+ /// concurrently modified by untrusted processes. Protecting against
+ /// TOCTOU races is out of scope for this crate.
+ ///
+ /// [`cap-std`]: https://docs.rs/cap-std/
///
/// # Examples
///
/// also be propagated to the path `dst`. Any existing file at the location
/// `dst` will be overwritten.
///
- /// This function carefully avoids writing outside of `dst`. If the file has
- /// a '..' in its path, this function will skip it and return false.
+ /// # Security
+ ///
+ /// See [`Archive::unpack`].
///
/// # Examples
///
// If the directory already exists just let it slide
fs::create_dir(dst).or_else(|err| {
if err.kind() == ErrorKind::AlreadyExists {
- let prev = fs::metadata(dst);
+ let prev = fs::symlink_metadata(dst);
if prev.map(|m| m.is_dir()).unwrap_or(false) {
return Ok(());
}